﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Tum.CollabXT.DocxGen.Resources;
using System.Data;

namespace Tum.CollabXT.DocxGen
{
    class DocumentGenerator
    {
        public string OutputPath
        {
            get { return _OutputPath; }
            set { _OutputPath = value; }
        }
        string _OutputPath;

        public string ProjectName
        {
            get { return _ProjectName; }
            set { _ProjectName = value; }
        }
        string _ProjectName;

        public string TemplateDocxPath
        {
            get { return _TemplateDocxPath; }
            set { _TemplateDocxPath = value; }
        }
        string _TemplateDocxPath;

        public string TemplateDotxPath
        {
            get { return _TemplateDotxPath; }
            set { _TemplateDotxPath = value; }
        }
        string _TemplateDotxPath;


        Log _Log = null;

        public DocumentGenerator(Log log)
        {
            if (log == null)
                throw new DocxGenException("Log must not be null");
            _Log = log;
        }

        /// <summary>
        /// The method createNewWorldDoc creates a simple .docx file
        /// </summary>
        /// <param name="strDocumentPath">document path.</param>
        public void GenerateDocuments(IProcessProvider processProvider, HashSet<string> productsToBeGenerated)
        {
            try
            {
                if (!Directory.Exists(_OutputPath))
                    Directory.CreateDirectory(_OutputPath);
            }
            catch
            {
                throw new DocxGenException("Could not create output directory");
            }

            List<string> names = new List<string>();

            foreach (string curProdId in productsToBeGenerated)
            {
                IProduct curProd = processProvider.GetProductByID(curProdId);
                if (curProd == null)
                    continue;

                bool docX = curProd.IsInitial;
                string tempDocumentPath = CopyTemplate(_OutputPath, docX);
                if (!string.IsNullOrEmpty(tempDocumentPath))
                {
                    try
                    {
                        Stream stream = new FileStream(tempDocumentPath, FileMode.Open, FileAccess.ReadWrite);
                        using (WordprocessingDocument wDocDocument = WordprocessingDocument.Open(stream, true))
                        {
                            MainDocumentPart mainPart = wDocDocument.MainDocumentPart;
                            // retrieve name and the folder name of the document
                            XmlDocument documentXML = new XmlDocument();
                            XmlNamespaceManager nsmng = new XmlNamespaceManager(documentXML.NameTable);
                            nsmng.AddNamespace("w", ParagraphUtils.NamespaceURL);
                            using (StreamReader streamMain = new StreamReader(mainPart.GetStream()))
                            {
                                documentXML.Load(streamMain);
                                streamMain.Close();
                            }

                            //Set header
                            IEnumerable<HeaderPart> headers = mainPart.HeaderParts;
                            XmlDocument headerXML = new XmlDocument();
                            foreach (HeaderPart hd in headers)
                            {
                                headerXML.Load(hd.GetStream());
                            }
                            //setHeaderPart()  (headerXML, names[1], string.Format("{0} - ", projectName));
                            {
                                XmlNamespaceManager _nsmng = new XmlNamespaceManager(headerXML.NameTable);
                                _nsmng.AddNamespace("w", ParagraphUtils.NamespaceURL);
                                CreateTIDs(headerXML, _nsmng);
                                ParagraphUtils utils = new ParagraphUtils(headerXML);
                                utils.SetTextByCustomTID(TIDs.ProductName, curProd.Name);
                                utils.SetTextByCustomTID(TIDs.ProjectNameHeader, _ProjectName + " - ");
                            }
                            foreach (HeaderPart hd in headers)
                                headerXML.Save(hd.GetStream());


                            //setMainPart()
                            {
                                names = new List<string>();

                                // retrive name and the folder name of the document
                                XmlNamespaceManager _nsmng = new XmlNamespaceManager(documentXML.NameTable);
                                _nsmng.AddNamespace("w", ParagraphUtils.NamespaceURL);
                                CreateTIDs(documentXML, _nsmng);

                                ModifyDocumentPartXML(documentXML, _ProjectName, curProd, processProvider); //!!!!!!!!!!!!!!!!!!!

                                string nameAndDiscipline = documentXML.SelectSingleNode(string.Format(Queries.GetTextByTid, TIDs.PrGroupandName), _nsmng).InnerText;

                                int seperatorIndex = nameAndDiscipline.IndexOf(":");
                                if (seperatorIndex >= 0)
                                {
                                    names.Add(nameAndDiscipline.Substring(0, seperatorIndex - 1));
                                    names.Add(nameAndDiscipline.Substring(seperatorIndex + 2));
                                }
                            }
                            //-------------

                            
                            Body body = new Body(documentXML.SelectSingleNode("//w:body", nsmng).OuterXml);
                            mainPart.Document = new Document(body);
                            mainPart.Document.Save();

                        }
                        stream.Close();
                    }
                    catch (DirectoryNotFoundException ex)
                    {
                        _Log.AddEntry(ex.Message);
                        return;
                    }
                    catch (UnauthorizedAccessException)
                    {
                        _Log.AddEntry("Directory not accessable. Process Cancelling!");
                        return;
                    }
                }
                else
                {
                    return;
                }

                if (names.Count >= 2 && docX)
                {
                    names[1] = string.Format("{0}.docx", names[1]);
                }
                if (names.Count >= 2 && !docX)
                {
                    names[1] = string.Format("{0}.dotx", names[1]);
                }
                ArangeFileLocationAndName(tempDocumentPath, names);
            }
        }

        /// <summary>
        /// Copies the template docx or the dotx file and .
        /// </summary>
        /// <param name="strPath">path to create the copy of the template.</param>
        /// <param name="dotX">if set to <c>true</c> copy of the dotx template created </param>
        /// <returns></returns>
        private string CopyTemplate(string path, bool docX)
        {
            string masterPath;
            if (docX)
                masterPath = TemplateDocxPath;
            else
                masterPath = TemplateDotxPath;

            string format = docX ? ".docx" : ".dotx";

            string tempFileName = "TEMPORARY";
            tempFileName = string.Format("{0}{1}", tempFileName, format);
            if (!(path.EndsWith("/")) && !(path.EndsWith(@"\")))
            {
                path = string.Format(@"{0}\{1}", path, tempFileName);
            }
            else
            {
                path = string.Format("{0}{1}", path, tempFileName);
            }
            try
            {
                try
                {
                    for (int counter = 2; counter < 1000; counter++)
                    {
                        if (!File.Exists(path))
                        {
                            File.Copy(masterPath, path);
                            return path;
                        }
                        else
                        {
                            if (path.Contains("(") && path.Contains(")") && path.IndexOf(")") > 0)
                            {
                                path = path.Remove(path.IndexOf("("), 3);
                            }
                            path = path.Insert(path.LastIndexOf("."), string.Format("({0})", counter.ToString()));
                        }
                    }
                    _Log.AddEntry("Filename is taken");
                    return null;
                }
                catch (DirectoryNotFoundException)
                {
                    _Log.AddEntry("Directory not found !");
                    return null;
                }
            }
            catch (UnauthorizedAccessException)
            {
                _Log.AddEntry("Cannot access the file !");
                return null;
            }
        }

        /// <summary>
        /// The method ArangeFileLocationAndName builds the right folder structure and gives to the documents the right name.
        /// </summary>
        /// <param name="strFilePath">file path.</param>
        /// <param name="names">array with th names.</param>
        private static void ArangeFileLocationAndName(string strFilePath, List<string> names)
        {
            string newFileName = string.Empty;
            string newFolderName = string.Empty;
            try
            {
                newFileName = NormalizeFileName(names[1]);
                newFolderName = NormalizeFileName(names[0]);

            }
            catch (ArgumentException e)
            {
                Console.Write(e.Message);
            }

            string newFolderRootPath = strFilePath.Remove(strFilePath.IndexOf("TEMPORARY"));
            string newFolderPath = (string.Format(@"{0}{1}", newFolderRootPath, newFolderName.TrimEnd()));

            try
            {
                if (!File.Exists(strFilePath))
                {
                    return;
                }
                if (!Directory.Exists(string.Format(@"{0}{1}", newFolderRootPath, newFolderName)))
                {
                    Directory.CreateDirectory(newFolderPath);
                }
                if (File.Exists(string.Format(@"{0}\{1}", newFolderPath, newFileName)))
                {
                    File.Delete(string.Format(@"{0}\{1}", newFolderPath, newFileName));
                }
                File.Move(strFilePath, string.Format(@"{0}\{1}", newFolderPath, newFileName));
            }
            catch (IOException)
            {
                int i = 0;
                while (File.Exists(string.Format(@"{0}\{1}", newFolderPath, newFileName)) && i < 10000)
                {
                    i++;
                    newFileName = IncrementNumber(newFileName);
                }
                File.Move(strFilePath, string.Format(@"{0}\{1}", newFolderPath, newFileName));
            }
        }

        /// <summary>
        /// NormalizeFileName removes all possible not allowed characters for the file name
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        private static string NormalizeFileName(string fileName)
        {
            string strResult = "Untitled_Folder";
            string[] notAllowedChars = { "/", @"\", ":", "|", "*", "?", "<", ">" };
            fileName.TrimEnd();
            for (int i = 0; i < notAllowedChars.Length; i++)
            {
                if (fileName.Contains(notAllowedChars[i]))
                {
                    strResult = fileName.Replace(notAllowedChars[i], " ");
                    return strResult.TrimEnd();
                }
            }
            return fileName;
        }

        private static string IncrementNumber(string fileName)
        {
            if ((fileName.LastIndexOf("(") == fileName.Length - 8) && (fileName.LastIndexOf(")") == (fileName.Length) - 6))
            {
                int i = Int16.Parse(fileName[(fileName.LastIndexOf("(") + 1)].ToString());
                i++;
                fileName = fileName.Replace(fileName[(fileName.LastIndexOf("(") + 1)].ToString(), i.ToString());
                return fileName;
            }
            else
            {
                fileName = string.Format("{0}{1}{2}", fileName.Substring(0, (fileName.Length) - 5), "(0)", fileName.Substring(fileName.LastIndexOf(".do")));
                return fileName;
            }
        }

        /// <summary>
        /// Given a document part of a docx file as XmlDocument object this method adds every <w:t> element 
        /// a w:customTID attribute for addresing these text elements and set their values. 
        /// In order to be unique the value of the w:customTID attribute is retrieved from the innertext from the node.
        /// </summary>
        /// <param name="documentXML">document XML.</param>
        /// <param name="nsmng">namespacemanager</param>
        private static void CreateTIDs(XmlDocument documentXML, XmlNamespaceManager nsmng)
        {
            XmlNodeList nodes = documentXML.SelectNodes(Queries.GetTextNodes, nsmng);

            foreach (XmlNode curNode in nodes)
            {
                if (curNode.InnerText.Contains(".") || curNode.InnerText.Contains("[") || curNode.InnerText.Contains("]"))
                {
                    XmlAttribute attr = documentXML.CreateAttribute("w", "customTID", ParagraphUtils.NamespaceURL);
                    attr.Value = curNode.InnerText.Trim();
                    curNode.Attributes.Append(attr);
                }
            }
        }

        /// <summary>
        /// Modifies the document part of the template docx file.
        /// </summary>
        /// <param name="documentPartXML">The document part XML.</param>
        /// <param name="row">The product row.</param>
        /// <returns>XmlDocument containing the modified document xml part</returns>
        public XmlDocument ModifyDocumentPartXML(XmlDocument documentPartXML, string projectName, IProduct product, IProcessProvider processProvider)
        {
            ParagraphUtils utils = new ParagraphUtils(documentPartXML);

            #region Set product name and discipline
            string disciplineName = "";
            foreach (IDiscipline curDis in processProvider.Disciplines)
            {
                if (curDis.Id.Equals(product.DisciplineId, StringComparison.OrdinalIgnoreCase))
                {
                    disciplineName = curDis.Name;
                    break;
                }
            }
            if (disciplineName.Length > 0)
                utils.SetTextByCustomTID(TIDs.PrGroupandName, string.Format("{0} : {1}", disciplineName, product.Name));
            else
                _Log.AddEntry("Product " + product.Name + " has no set discipline.", LogEntryType.Warning); 
            #endregion

            #region Set responsibles
            ///TODO: Multiple responsibles correct?

            //Build responsibles string
            IRole[] responsibleRoles = processProvider.GetRolesResponsibleByProductID(product.Id);
            if (responsibleRoles.Length > 0)
            {
                StringBuilder responsiblesStringBuilder = new StringBuilder();
                foreach (IRole curResp in responsibleRoles)
                {
                    if (responsiblesStringBuilder.Length > 0)
                        responsiblesStringBuilder.Append(", ");
                    responsiblesStringBuilder.Append(curResp.Name);
                }

                utils.SetTextByCustomTID(TIDs.PrResponsible, responsiblesStringBuilder.ToString());
            }
            else
            {
                _Log.AddEntry("Product " + product.Name + " has no one responsible.", LogEntryType.Warning);
                utils.RemovePbyTID(TIDs.PrResponsible);
            }
            #endregion

            #region Set involved roles
            ///TODO: Bereits geschriebene Rollenverantwortliche als Mitbearbeiter nochmals nennen?

            IRole[] involvedRoles = processProvider.GetRolesParticipatingByProductID(product.Id);
            if (involvedRoles.Length > 0)
            {
                string[] roleNames = new string[involvedRoles.Length];
                for (int a = 0; a < involvedRoles.Length; a++)
                    roleNames[a] = involvedRoles[a].Name;
                utils.CloneAndEvaluatePs(TIDs.ActorName, roleNames, true, false, 0);
            }
            else
            {
                _Log.AddEntry(string.Format("Product {0} has no associated roles.", product.Name), LogEntryType.Warning);
                utils.RemovePbyTID(TIDs.ActorName);
            }
            #endregion

            #region Set creation
            if (product.IsExternal && product.IsExternal)
            {
                utils.CloneAndEvaluatePs(TIDs.CreationAtt, new string[] { "Initial", "Extern" }, false, false, 0);
            }
            else
            {
                if (product.IsExternal)
                    utils.SetTextByCustomTID(TIDs.CreationAtt, "Extern");
                if (product.IsInitial)
                    utils.SetTextByCustomTID(TIDs.CreationAtt, "Initial");
            }
            #endregion

            #region Set product description
            if (product.ExtendedData.ContainsKey("DescriptionRaw") && product.ExtendedData["DescriptionRaw"] is string)
                utils.CloneAndEvaluateCDATA(TIDs.TextModule, product.ExtendedData["DescriptionRaw"] as string, false, 0);
            else
                utils.CloneAndEvaluatePs(TIDs.TextModule, new string[] { product.Description }, false, false, 0);
            #endregion

            //Set project name
            utils.SetTextByCustomTID(TIDs.ProjectName, _ProjectName);

            SetProductTopics(utils, product, processProvider);
            SetProductContentDependencies(utils, product, processProvider);
            SetProductCreationDependencies(utils, product, processProvider);

            CleanUpPlaceHolders(utils, product);

            return documentPartXML;
        }

        private void CleanUpPlaceHolders(ParagraphUtils utils, IProduct product)
        {
            utils.RemovePbyTID(TIDs.ActorsBegin);
            utils.RemovePbyTID(TIDs.ActorsEnd);
            utils.RemovePbyTID(TIDs.ContentName);
            utils.RemovePbyTID(TIDs.ProductContentsBegin);
            utils.RemovePbyTID(TIDs.ContentDescription);
            utils.RemovePbyTID(TIDs.DependencDescTitle);
            utils.RemovePbyTID(TIDs.DependenciesBegin);
            utils.RemovePbyTID(TIDs.DependenciesEnd);
            utils.RemovePbyTID(TIDs.DependencyDescription);
            utils.RemovePbyTID(TIDs.DependencyName);
            utils.RemovePbyTID(TIDs.DependencyPrTitle);
            utils.RemovePbyTID(TIDs.DependentPrBegin);
            utils.RemovePbyTID(TIDs.DependentPrEnd);
            utils.RemovePbyTID(TIDs.DependentPrName);
            utils.RemovePbyTID(TIDs.DetailValuesEnd);
            utils.RemovePbyTID(TIDs.TextModule);
            utils.RemovePbyTID(TIDs.CreationAttBeginn);
            utils.RemovePbyTID(TIDs.CreationEnd);
            utils.RemovePbyTID(TIDs.CreationBegin);
            utils.RemovePbyTID(TIDs.CreationEnd);
            utils.RemovePbyTID(TIDs.CreationSourceBegin);
            utils.RemovePbyTID(TIDs.CreationSourceEnd);
            utils.RemovePbyTID(TIDs.CreationAttEnd);
            utils.RemovePbyTID(TIDs.CreationOtherProduct);
            utils.RemovePbyTID(TIDs.CreationName);
            utils.RemovePbyTID(TIDs.SampleText);
            utils.RemovePbyTID(TIDs.DetailsValuesBegin);
            utils.RemovePbyTID(TIDs.DetailValuesEnd);
            utils.RemovePbyTID(TIDs.DetailsValue);
            utils.RemovePbyTID(TIDs.ProjectDetailTitle);
            utils.RemovePbyTID(TIDs.ContentsEnd);
            utils.RemovePbyTID(TIDs.ProjectDetailsEnd);
            utils.RemovePbyTID(TIDs.ProjectDetailsBegin);

            if (!product.IsInitial || product.IsExternal)
                utils.RemovePbyTID(TIDs.CreationAtt);
        }

        class CollectedProductContentDependency
        {
            public string Name;
            public string Description;
            public List<string> DependentProductNames = new List<string>();
        }
        void SetProductContentDependencies(ParagraphUtils utils, IProduct product, IProcessProvider processProvider)
        {
            Dictionary<string, CollectedProductContentDependency> collectedDependencies = new Dictionary<string, CollectedProductContentDependency>();

            try
            {
                foreach (IProcessElementDependency curDep in processProvider.Dependencies)
                {
                    if (curDep is ProductContentDependency)
                    {
                        ProductContentDependency curProdDependency = curDep as ProductContentDependency;

                        if (curProdDependency.ProductId.Equals(product.Id, StringComparison.OrdinalIgnoreCase))
                        {
                            CollectedProductContentDependency curCollectedDependency;
                            if (collectedDependencies.ContainsKey(curProdDependency.Id))
                                curCollectedDependency = collectedDependencies[curProdDependency.Id];
                            else
                            {
                                curCollectedDependency = new CollectedProductContentDependency();
                                curCollectedDependency.Name = curProdDependency.Name;
                                
                                if (curProdDependency.ExtendedData.ContainsKey("DescriptionRaw") && curProdDependency.ExtendedData["DescriptionRaw"] is string)
                                    curCollectedDependency.Description = curProdDependency.ExtendedData["DescriptionRaw"] as string;
                                else
                                    curCollectedDependency.Description = curProdDependency.Description;

                                curCollectedDependency.DependentProductNames.Add(product.Name);
                                collectedDependencies.Add(curProdDependency.Id, curCollectedDependency);
                            }

                            curCollectedDependency.DependentProductNames.Add(processProvider.GetProductByID(curProdDependency.DependentProductId).Name);
                        }
                    }
                }

                string[] depNames = new string[collectedDependencies.Count];
                int i = collectedDependencies.Count - 1;
                foreach (var a in collectedDependencies)
                {
                    depNames[i] = a.Value.Name;

                    i--;
                }

                if (collectedDependencies.Count > 0)
                {
                    //Write strings to document.
                    utils.CloneAndEvaluatePs(TIDs.DependencyName, depNames, false, true, 0);

                    ///TODO: Localize!
                    utils.CloneAndEvaluatePs(TIDs.DependencDescTitle, "Beschreibung:", collectedDependencies.Count, false, true, 0);
                    utils.CloneAndEvaluatePs(TIDs.DependencyPrTitle, "Betroffene Podukte:", collectedDependencies.Count, false, true, 0);

                    i = collectedDependencies.Count - 1;
                    foreach (var a in collectedDependencies)
                    {
                        utils.CloneAndEvaluatePs(TIDs.DependentPrName, a.Value.DependentProductNames.ToArray(), false, false, i + 1);
                        utils.MoveParagraph(utils.SelectByTIDs(TIDs.DependentPrName, i + 1), utils.SelectByTIDs(TIDs.DependencyName, i + 1));
                        utils.MoveParagraph(utils.SelectByTIDs(TIDs.DependencyPrTitle, i + 1), utils.SelectByTIDs(TIDs.DependencyName, i + 1));
                        utils.CloneAndEvaluateCDATA(TIDs.DependencyDescription, a.Value.Description, false, i + 1);
                        utils.MoveParagraph(utils.SelectByTIDs(TIDs.DependencyDescription, i + 1), utils.SelectByTIDs(TIDs.DependentPrName, i + 1));
                        utils.MoveParagraph(utils.SelectByTIDs(TIDs.DependencDescTitle, i + 1), utils.SelectByTIDs(TIDs.DependentPrName, i + 1));
                        i--;
                    }
                }
            }
            catch
            {
                _Log.AddEntry("Product content dependencies missing for \"" + product.Name + "\".", LogEntryType.Warning);
                utils.RemovePbyTID(TIDs.DependentPrName);
                utils.RemovePbyTID(TIDs.DependentPrName);
                utils.RemovePbyTID(TIDs.DependencyPrTitle);
                utils.RemovePbyTID(TIDs.DependencyDescription);
                utils.RemovePbyTID(TIDs.DependencyDescription);
                utils.RemovePbyTID(TIDs.DependencDescTitle);
            }
        }

        void SetProductCreationDependencies(ParagraphUtils utils, IProduct product, IProcessProvider processProvider)
        {
            if (!product.IsInitial)
            {
                List<string> depNameList = new List<string>();
                List<string[]> depProdNameList = new List<string[]>();

                HashSet<string> alreadyWrittenDependencies = new HashSet<string>();

                try
                {
                    foreach (IProcessElementDependency curDep in processProvider.Dependencies)
                    {
                        if (curDep is GeneratingProductDependency)
                        {
                            GeneratingProductDependency curProdDependency = curDep as GeneratingProductDependency;

                            if (curProdDependency.DependentProductId.Equals(product.Id, StringComparison.OrdinalIgnoreCase))
                            {
                                if (!alreadyWrittenDependencies.Contains(curProdDependency.Id)) //Do not write dependencies more than once
                                {
                                    alreadyWrittenDependencies.Add(curProdDependency.Id); //Save that this dependencies has been written
                                    depNameList.Add(curProdDependency.Name);




                                    List<IProduct> products = new List<IProduct>();
                                    foreach (IProcessElementDependency curDep2 in processProvider.Dependencies)
                                    {
                                        if (curDep2 is GeneratingProductDependency)
                                        {
                                            GeneratingProductDependency curProdDependency2 = curDep2 as GeneratingProductDependency;
                                            if (curProdDependency2.DependentProductId.Equals(product.Id, StringComparison.OrdinalIgnoreCase))
                                            {
                                                IProduct prod = processProvider.GetProductByID(curProdDependency2.ProductId);
                                                if (prod != null)
                                                    products.Add(prod);
                                            }
                                        }
                                    }




                                    //Enum dependent product names
                                    IProduct[] dependentProducts = products.ToArray();//processProvider.GetGeneratedProductsByProductID(product.Id);
                                    string[] dependentProductNames = new string[dependentProducts.Length];
                                    for (int a = 0; a < dependentProducts.Length; a++)
                                        dependentProductNames[a] = dependentProducts[a].Name;
                                    depProdNameList.Add(dependentProductNames);

                                    //Write strings to document.
                                    utils.CloneAndEvaluatePs(TIDs.CreationName, depNameList.ToArray(), false, true, 0);
                                    for (int i = depProdNameList.Count - 1; i >= 0; i--)
                                    {
                                        utils.CloneAndEvaluatePs(TIDs.CreationOtherProduct, depProdNameList[i], false, false, i + 1);
                                        utils.MoveParagraph(utils.SelectByTIDs(TIDs.CreationOtherProduct, i + 1), utils.SelectByTIDs(TIDs.CreationName, i + 1));
                                    }
                                }
                            }
                        }
                    }
                }
                catch
                {
                    _Log.AddEntry("Product creation dependencies missing for \"" + product.Name + "\".", LogEntryType.Warning);    
                    utils.RemovePbyTID(TIDs.CreationName);
                    utils.RemovePbyTID(TIDs.CreationOtherProduct);
                }
            }
        }

        void SetProductTopics(ParagraphUtils utils, IProduct product, IProcessProvider processProvider)
        {
            try
            {
                ITopic[] productTopics = processProvider.GetTopicsByProductID(product.Id);
                string[] titleArr = new string[productTopics.Length];
                string[] contentArr = new string[productTopics.Length];
                for (int a = 0; a < productTopics.Length; a++)
                {
                    titleArr[a] = productTopics[a].Name;
                    if (productTopics[a].ExtendedData.ContainsKey("DescriptionRaw") && productTopics[a].ExtendedData["DescriptionRaw"] is string)
                        contentArr[a] = productTopics[a].ExtendedData["DescriptionRaw"] as string;
                    else
                        contentArr[a] = productTopics[a].Description;

                }

                utils.CloneAndEvaluatePs(TIDs.ContentName, titleArr, false, true, 0);
                for (int i = contentArr.Length - 1; i >= 0; i--)
                {
                    utils.CloneAndEvaluateCDATA(TIDs.ContentDescription, contentArr[i], false, i + 1);
                    utils.MoveParagraph(utils.SelectByTIDs(TIDs.ContentDescription, i + 1), utils.SelectByTIDs(TIDs.ContentName, i + 1));
                }
            }
            catch { }
        }
    }
}